/* $Id: telnet.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $
 *
 * FILE       : telnet.cpp
 * AUTHOR     : unknown (sources found on www.telnet.org)
 * PROJECT    : ReactOS Operating System
 * DESCRIPTION: telnet client for the W32 subsystem
 * DATE       : 2001-01-21
 * REVISIONS
 *	2001-02-21 ea	Modified to compile under 0.0.16 src tree
 */
#include <winsock.h>
#include <windows.h>
#include <stdio.h>

#include "../logger.h"
#include "telnet.h"
#include "console.h"

#define  MAX_SOCKET_BUFFER  2*1024 //2k
HANDLE                 telnet_recv_thread;
SOCKET                 telnet_server_s = 0;
extern TELNET_PROPS    telnetPropsObject;

// delete ... 
extern TELNET_STATUS telnet_current_status;
extern char *telnet_username;
extern char *telnet_password;

CRITICAL_SECTION    telnet_mutex;

bool scan_input_string(char * string)
{
	char * cp;

	for (cp=string; *cp; ++cp)
	{
		if ('A' <= *cp && *cp <= 'Z')
			*cp += 'a' - 'A';
	}

	return true;
}

#define MAX_SOCKET_BUFFER_SIZE 512

//
// sock_loop is the thread dedicatd to reading socket input.
// It waits for data from the socket, and then gives it one byte at a time
// to the telnet vm to process.
//
DWORD sock_loop(SOCKET server)
{
	char buf[MAX_SOCKET_BUFFER_SIZE+1];
	unsigned long read;
	char* scan;

	while( (telnet_server_s) && (read = recv(server,buf,MAX_SOCKET_BUFFER_SIZE,0)) && read != SOCKET_ERROR )
	{
		int size = read;
		scan = buf;
		while(read--)
			vm(server, *scan++);
		
		// skip telnet subnegotiation 
		if ( telnetPropsObject.status != eData ) continue;

		buf[size]=0;
		if ( !scan_input_string(buf) ) continue;
		
		// check if router returns echo string
		if ( strstr(buf,  telnetPropsObject.prompt)!=NULL ) {
			telnetPropsObject.status = eMessage;
			continue;
		}

		vector<TELNET_META>::iterator begin = telnetPropsObject.metaData.begin();
		vector<TELNET_META>::iterator end   = telnetPropsObject.metaData.end();
		
		for (; begin!=end; begin++) {
			if ( strstr(buf, (*begin).keyword.c_str())!=NULL ) {
				telnet_send((*begin).value.c_str(), strlen((*begin).value.c_str()));
			}
		}
	}

	return WSAGetLastError();
}

//DWORD input_loop(SOCKET server)
//{
//  char buf[256];
//  DWORD read;
///*
//  do
//  {
//    WaitForSingleObject(StandardInput, INFINITE);
//    ReadFile(StandardInput, buf, sizeof buf, & read, NULL);
//  }
//  while(SOCKET_ERROR != send(server, buf, read, 0));
//*/
//  return 0;
//
//}

// thread safe
bool telnet_send(const char *buf, int len)
{
	if ( 
		buf==NULL || 
		telnet_server_s==0 ||
		telnet_recv_thread==NULL )
	{
		NM_ERROR("telnet_send: buf=%x, telnet_server_s=%d, telnet_recv_thread=%x\n",
			buf, telnet_server_s, telnet_recv_thread);
		return false;
	}

	EnterCriticalSection(&telnet_mutex);

	char sock_buffer[MAX_SOCKET_BUFFER] = {0};
	if ( len>MAX_SOCKET_BUFFER-1 ) {
		NM_ERROR("telnet_send: len=%d\n", len);
		return false;
	}

	memcpy(sock_buffer, buf, len);
	sock_buffer[len] = '\r';
	int ret = send(telnet_server_s, sock_buffer, len+1, 0);

	telnetPropsObject.status = eHold;
	NM_TRACE("Telnet command:\n%s\n", buf);
	LeaveCriticalSection(&telnet_mutex);
	return ret>0 ? true : false;
}

void close_telnet()
{
	if (telnet_server_s==0 || telnet_recv_thread==NULL) return;

	WaitForSingleObject(telnet_recv_thread, 1000);
	CloseHandle(telnet_recv_thread);
	telnet_recv_thread = NULL;

	closesocket(telnet_server_s);
	telnet_server_s = NULL;

	DeleteCriticalSection(&telnet_mutex);
	NM_DEBUG("telnet client thread exits successfully\n");
}

void telnet(SOCKET server)
{
	DWORD dwThreadIdsock;
//	DWORD dwThreadIdinput;

	// Initialize ...
	telnetPropsObject.status = eInit;
	telnet_recv_thread = CreateThread(
		NULL,                        /* no security attributes        */
		0,                           /* use default stack size        */
		(LPTHREAD_START_ROUTINE)sock_loop,  /* thread function       */
		(LPVOID)server,              /* argument to thread function   */
		0,                           /* use default creation flags    */
		&dwThreadIdsock);            /* returns the thread identifier */

	InitializeCriticalSection(&telnet_mutex);

	//wait for the other thread to complete any setup negotiation...
	//Sleep(500); //- this is not the problem - its just bloody stuffing up!

	//telnet_threads[1] = CreateThread(
	//  NULL,                        /* no security attributes        */
	//  0,                           /* use default stack size        */
	//  (LPTHREAD_START_ROUTINE) input_loop, /* thread function       */
	//  (LPVOID)server,              /* argument to thread function   */
	//  0,                           /* use default creation flags    */
	//  &dwThreadIdinput);           /* returns the thread identifier */
}


//
// connect to the hostname,port
//
bool telnet(char const* pszHostName, const short nPort)
{
	unsigned long ip;
	if ((*pszHostName <= '9') && (*pszHostName >= '0'))
	{
		if ((ip = inet_addr(pszHostName)) == INADDR_NONE)
			err("invalid host IP address given");
	}
	else
	{
		hostent* ent = gethostbyname(pszHostName);
		if (!ent) goto __ERROR__;
		ip = *(unsigned long*)(ent->h_addr);
	}

	sockaddr_in name;
	name.sin_family = AF_INET;
	name.sin_port = htons(nPort);
	name.sin_addr = *(in_addr*)&ip;

	console_title_connecting (pszHostName, nPort);

	if( (telnet_server_s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET ) goto __ERROR__;
	if( SOCKET_ERROR == connect(telnet_server_s,(sockaddr*)&name,sizeof(sockaddr)) ) goto __ERROR__;

	console_title_connected (pszHostName, nPort);
	telnet(telnet_server_s);
	return true;

__ERROR__:
	err(sockmsg(WSAGetLastError()));
	return false;
}

// when disconnect, reconnect...
bool telnet_reconnect()
{

	return true;
}

void err(char const* s, ...)
{
	char buf[256];
	_snprintf(buf, 256, "%s\n\0", s);
	printf(buf);
	NM_ERROR(buf);
}

/* EOF */